
#include <asm-generic/errno-base.h>
#include <asm-generic/errno.h>
#include <asm-generic/socket.h>
#include <bits/types/struct_iovec.h>
#include <bits/types/struct_timeval.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <fcntl.h>
#include <sys/wait.h>
#include <time.h>

#include "my_net.h"

enum CHILD_STATUS {

    FREE = 0,
    BUSY
};

typedef struct sock_node {

    struct sock_node * next;
    int fd;

}sock_node;

typedef struct unit_child {

    int child_index_;
    pid_t child_pid_;
    int child_pipefd_[2];
    enum CHILD_STATUS child_status_;
    int count_;

    struct sock_node * sock_list_;

}unit_child;

typedef struct child_link_node {

    struct child_link_node * next;
    struct child_link_node * prev;
    unit_child child_process;

}child_link_node;

typedef struct child_link_list {

    struct child_link_node * head;
    struct child_link_node * end;
    struct child_link_node * now_curr;

} child_link_list;


void child_main(unit_child * child);

void child_sig_handle(int);

int read_fd(int fd);

void write_fd(int fd, int send_fd);

// gcc -Wall -Wshadow -g master_cluster.c tcp_listen.c -o master_cluster -I./

int main(int argc, char ** argv) {

    int set_max = -1;
    int pipefd[2];
    pipe(pipefd);


    if (pipefd[0] > pipefd[1]) {

        set_max = pipefd[0];
    }
    socklen_t len;
    int listenfd = TcpListen("192.168.31.226", "8010", &len);

    if (listenfd > set_max) {

        set_max = listenfd;
    }
    fcntl(listenfd, F_SETFL, O_NONBLOCK);

    // craete unit child struct 
    int child_number = 15;
    // unit_child * child_ptr = (unit_child * )malloc(sizeof(unit_child) * child_number);
    child_link_list list;

    list.head = NULL;
    list.end  = NULL;
    list.now_curr = NULL;

    for (int index = 0; index < child_number; index++) {


        child_link_node * node = (child_link_node*)malloc(sizeof(child_link_node));

        node->next = NULL;
        node->prev = NULL;

        if (list.head == NULL) {

            list.head = node;
            list.end  = node;

            list.head->prev = node;
            list.head->next = node;

            list.end->next = list.head;
            list.end->prev = list.head;

        } else {

            node->next = list.head;
            list.head->prev = node;

            list.end->next = node;
            node->prev      = list.end;

            list.head = node;
        }
        pipe(node->child_process.child_pipefd_);
        socketpair(PF_UNIX, SOCK_DGRAM, 0, node->child_process.child_pipefd_);
        node->child_process.child_status_ = FREE;
        node->child_process.child_index_  = index;
        node->child_process.count_        = 0;
        
        // fcntl(node->child_process.child_pipefd_[0], F_SETFL, O_NONBLOCK);

        if ((node->child_process.child_pid_ = fork()) == 0) {

            close(listenfd);
            close(pipefd[0]);
            printf("child index : %d\n", index);
            child_main(&node->child_process);
            return 0;
        }
    }

    fd_set read_set;
    FD_ZERO(&read_set);
    for (;;) {

        FD_SET(listenfd, &read_set);
        int number = select(set_max + 1, &read_set, NULL, NULL, NULL);

        if (number == 0) {

            if (errno == EINTR) {

                continue;
            } else {

                printf("has errno has happed : %d will send sign to child process\n", errno);
                break;
            }
        }
        if (FD_ISSET(listenfd, &read_set)) {

            int connectfd = accept(listenfd, NULL, NULL);
            if (list.now_curr == NULL) {

                list.now_curr = list.head;
            }
            // printf("send connectfd to index : %d\n", list.now_curr->child_process.child_index_);
            write_fd(list.now_curr->child_process.child_pipefd_[1], connectfd);
            list.now_curr = list.now_curr->next;
            close(connectfd);
        }
        FD_ZERO(&read_set);
    }
    child_link_node * ptr = list.head;
    while (ptr) {

        child_link_node * delete_node = ptr;
        delete_node->prev = NULL;
        ptr = ptr->next;
        free(delete_node);
    }
    close(listenfd);
    return EXIT_SUCCESS;
}

void child_main(unit_child * child) {

    // add signal handle
    signal(SIGINT, child_sig_handle);
    signal(SIGTERM, child_sig_handle);
    int max = child->child_pipefd_[0];
    char buf[1024];
    fd_set read_set;
    fd_set execption_set;
    fd_set sock_read_set;

    FD_ZERO(&read_set);
    FD_ZERO(&sock_read_set);

    struct timeval time;
    bzero(&time, sizeof(time));

    while (1) {

        time.tv_sec = 0;
        time.tv_usec = 1000;
        FD_ZERO(&read_set);
        FD_SET(child->child_pipefd_[0], &read_set);
        int number = select(child->child_pipefd_[0] + 1, &read_set, NULL, NULL, &time);
        if (number <= 0) {

            if (errno == EINTR || errno == ETIMEDOUT || number == 0) {
            

            } else {

                break;
            }
        }
        if (FD_ISSET(child->child_pipefd_[0], &read_set)) {

            int fd = read_fd(child->child_pipefd_[0]);
            child->child_status_ = BUSY;
            if (fd ==  -1) {

                printf("%s %d errno : %d\n",__FUNCTION__, __LINE__, errno);
                child->child_status_ = FREE;
                continue;
            }
            sock_node * node = (sock_node *)malloc(sizeof(sock_node));
            node->fd = fd;
            node->next = child->sock_list_;
            child->sock_list_ = node;
        }
        FD_ZERO(&sock_read_set);
        child->count_ = 0;
        for (sock_node * ptr = child->sock_list_; ptr != NULL; ptr = ptr->next) {

            FD_SET(ptr->fd, &sock_read_set);
            child->count_ = child->count_ > ptr->fd ? child->count_ : ptr->fd;
        }
        time.tv_sec = 0;
        time.tv_usec = 1000;

        number = select(child->count_ + 1, &sock_read_set, NULL, NULL, &time);
        if (number <= 0) {

            if (errno == EINTR || errno == ETIMEDOUT || number == 0) {
            
                // continue;
            } else {

                break;
            }
        }
        sock_node * back_ptr = child->sock_list_;
        for (sock_node * ptr = child->sock_list_; ptr != NULL; ) {

            // if (number <= 0) {
            //     break;
            // }
            if (FD_ISSET(ptr->fd, &sock_read_set)) {

                number--;
                bzero(buf, sizeof(buf));
                if (recv(ptr->fd, buf, sizeof(buf), 0) == 0) {
                    
                    printf("remove sockfd : %d\n", ptr->fd);
                    if (ptr == child->sock_list_) {

                        child->sock_list_ = child->sock_list_->next;
                        close(ptr->fd);
                        free(ptr);
                        ptr = child->sock_list_;
                        continue;
                    }
                    back_ptr->next = ptr->next;
                    close(ptr->fd);
                    free(ptr);
                    ptr = back_ptr->next;
                    continue;

                } else {

                    printf("recv message : %s\n", buf);
                    send(ptr->fd, buf, strlen(buf), 0);
                }
            } 
            back_ptr = ptr;
            ptr = ptr->next;
        }    
    }
}

void child_sig_handle(int no) {

    printf("pid : %d recevice signal\n", getpid());
    exit(1);
}

int read_fd(int fd) {

    char buf[10];

    struct iovec iov;
    
    struct cmsghdr cmsg;

    struct msghdr msg;

    bzero(&msg, sizeof(msg));
    
    bzero(buf, sizeof(buf));
    iov.iov_base = buf;
    iov.iov_len = sizeof(buf);

    msg.msg_name = NULL;
    msg.msg_namelen = 0;

    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    cmsg.cmsg_len = CMSG_LEN(sizeof(fd));

    msg.msg_control = &cmsg;
    msg.msg_controllen = CMSG_LEN(sizeof(fd));

    // recvmsg(int __fd, struct msghdr *__message, int __flags)
    ssize_t size = recvmsg(fd, &msg, 0);
    if (size == 0) {

        return -1;
    }
    if (cmsg.cmsg_level != SOL_SOCKET || cmsg.cmsg_type != SCM_RIGHTS) {

        printf("cmsg.cmsg_level : %d and cmsg.cmsg_type : %d\n", cmsg.cmsg_level, cmsg.cmsg_type);
        return -1;
    }
    int recv_fd = *(int *)CMSG_DATA(&cmsg);

    return recv_fd;
}

void write_fd(int fd, int send_fd) {

    struct iovec iov;

    struct msghdr msg;

    bzero(&msg, sizeof(msg));

    struct cmsghdr cmsg;

    bzero(&cmsg, sizeof(cmsg));

    char buf[10];
    bzero(buf, sizeof(buf));
    iov.iov_base = buf;
    iov.iov_len  = sizeof(buf);

    cmsg.cmsg_level = SOL_SOCKET;
    cmsg.cmsg_type  = SCM_RIGHTS;
    cmsg.cmsg_len   = CMSG_LEN(sizeof(send_fd));

    *(int *)CMSG_DATA(&cmsg) = send_fd;

    msg.msg_iov    = &iov;
    msg.msg_iovlen = 1;

    msg.msg_name    = NULL;
    msg.msg_namelen = 0;

    msg.msg_control = &cmsg;
    msg.msg_controllen = CMSG_LEN(sizeof(send_fd));

    // printf("send fd result : %ld\n",sendmsg(fd, &msg, 0));
    sendmsg(fd, &msg, 0);
}
